<template>
  <CesiumBox ref="container"></CesiumBox>
</template>

<script setup>
import CesiumBox from "../../components/CesiumBox.vue";
import { ref, onMounted } from "vue";
import * as Cesium from "cesium";

const container = ref(null);
let cesiumPara = null;

class DrawCommand {
  constructor(modelMatrix) {
    this.modelMatrix = modelMatrix || Cesium.Matrix4.IDENTITY.clone();
    this.drawCommand = null;
  }

  createCommand(context) {
    let modelMatrix = this.modelMatrix;

    let box = new Cesium.BoxGeometry({
      vertexFormat: Cesium.VertexFormat.POSITION_ONLY,
      maximum: new Cesium.Cartesian3(200000.0, 200000.0, 200000.0),
      minimum: new Cesium.Cartesian3(0, 0, 0),
    });
    let geometry = Cesium.BoxGeometry.createGeometry(box);

    let attributeLocations =
      Cesium.GeometryPipeline.createAttributeLocations(geometry);

    let va = Cesium.VertexArray.fromGeometry({
      context: context,
      geometry: geometry,
      attributeLocations: attributeLocations,
    });

    let vs = `
        attribute vec3 position;
        varying vec4 v_positionEC;
        void main(){
          v_positionEC = vec4(position, 1.0);
          gl_Position = czm_projection * czm_modelView * vec4(position, 1.0);
        }
      `;

    let fs = `
        uniform vec3 color;
        uniform float time;
        varying vec4 v_positionEC;
        void main(){
          vec3 color = mix(vec3(1,0,0), vec3(0,1,0), 1.0 - fract(v_positionEC.z / 200000.0));
          gl_FragColor = vec4(1.0 - time, time, 0, 1.0);
        }
      `;

    let shaderProgram = Cesium.ShaderProgram.fromCache({
      context: context,
      vertexShaderSource: vs,
      fragmentShaderSource: fs,
      attributeLocations: attributeLocations,
    });

    let oldTime = new Date().getTime();
    let uniformMap = {
      color() {
        return Cesium.Color.GRAY;
      },
      time() {
        let nowTime = ((new Date().getTime() - oldTime) % 2000) / 2000;
        return nowTime;
      },
    };

    let renderState = Cesium.RenderState.fromCache({
      cull: {
        enabled: true,
        face: Cesium.CullFace.BACK,
      },
      depthTest: {
        enabled: true,
      },
    });

    this.drawCommand = new Cesium.DrawCommand({
      modelMatrix: modelMatrix,
      vertexArray: va,
      shaderProgram: shaderProgram,
      uniformMap: uniformMap,
      renderState: renderState,
      pass: Cesium.Pass.OPAQUE,
    });
  }

  update(frameState) {
    if (!this.drawCommand) {
      this.createCommand(frameState.context);
    }
    frameState.commandList.push(this.drawCommand);
  }
}

function addCustomCube() {
  let modelMatrix = Cesium.Matrix4.multiplyByTranslation(
    Cesium.Transforms.eastNorthUpToFixedFrame(
      Cesium.Cartesian3.fromDegrees(114.5, 44.5, 0)
    ),
    new Cesium.Cartesian3(0.0, 0.0, 100000.0),
    new Cesium.Matrix4()
  );

  let geometry = new Cesium.GeometryInstance({
    id: "box with height",
    geometry: new Cesium.CylinderGeometry({
      length: 200000,
      topRadius: 200000,
      bottomRadius: 200000,
      slices: 4,
    }),
    modelMatrix: modelMatrix,
  });

  let material = new Cesium.Material.fromType("Color");
  material.uniforms.color = Cesium.Color.WHITE;

  let vertex = `
      attribute vec3 position3DHigh;
      attribute vec3 position3DLow;
      attribute vec3 normal;
      attribute vec2 st;
      attribute float batchId;
      varying vec4 v_positionEC;
      varying vec3 v_normalEC;
      varying vec2 v_st;
      void main() {
        vec4 p = czm_computePosition();
        vec4 eyePosition = czm_modelViewRelativeToEye * p;
        v_positionEC = czm_inverseModelView * eyePosition;
        v_normalEC = czm_normal * normal;
        v_st = st;
        gl_Position = czm_modelViewProjectionRelativeToEye * p;
      }
    `;

  let fragment = `
      varying vec4 v_positionEC;
      varying vec3 v_normal;
      void main() {
        float l = sqrt(pow(v_positionEC.x, 2.0) + pow(v_positionEC.y, 2.0) + pow(v_positionEC.z, 2.0));
        float cy = fract(abs(v_positionEC.z - 100000.0) / 200000.0);
        gl_FragColor = vec4(cy, 1.0 - cy, 0.0, 1.0);
      }
    `;

  let appearance = new Cesium.MaterialAppearance({
    closed: true,
    translucent: true,
    material: material,
    vertexShaderSource: vertex,
    fragmentShaderSource: fragment,
  });

  cesiumPara.viewer.scene.primitives.add(
    new Cesium.Primitive({
      geometryInstances: [geometry],
      appearance: appearance,
      releaseGeometryInstances: false,
      compressVertices: false,
    })
  );
}

function addCube() {
  addCustomCube();

  let origin = Cesium.Cartesian3.fromDegrees(120, 46, 0);
  let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);

  let primitive = new DrawCommand(modelMatrix);
  cesiumPara.viewer.scene.primitives.add(primitive);
}

onMounted(() => {
  cesiumPara = container.value.cesiumPara;
  addCube();

  cesiumPara.viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(
      125.59719837773976,
      34.511151215544444,
      1438496.33035057
    ),
    orientation: {
      heading: 5.900144937823217,
      pitch: -0.8260025319357904,
      roll: 6.282441569785731,
    },
    duration: 3,
  });
});
</script>
